package com.zhouchj.elasticsearch.query;

import com.zhouchj.elasticsearch.basic.EsFieldProcessor;
import com.zhouchj.elasticsearch.basic.EsQueryBuilderUtil;
import com.zhouchj.elasticsearch.basic.SFunction;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;

import java.time.Instant;
import java.util.Collections;
import java.util.List;

/**
 * Es基础查询构建
 *
 * @author zhouchj
 * @date 2023/8/24
 */
public class EsBasicQuery<T, Column extends SFunction<T, ?>, Children extends EsBasicQuery<T, Column, Children>>
        extends EsFieldProcessor<T> {

    /**
     * 子类型 方便返回this
     */
    protected final Children typedThis = (Children) this;
    /**
     * 索引
     */
    protected final IndexCoordinates index;
    /**
     * es template
     */
    protected final ElasticsearchRestTemplate elasticsearchRestTemplate;
    /**
     * es查询条信息(包括分页、查询条件、聚合)
     */
    protected NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
    /**
     * 查询条件
     */
    protected BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

    protected EsBasicQuery(Class<T> clazz,
                           IndexCoordinates index,
                           ElasticsearchRestTemplate elasticsearchRestTemplate) {
        super(clazz);
        this.index = index;
        this.elasticsearchRestTemplate = elasticsearchRestTemplate;
    }

    /**
     * 精确查询
     *
     * @param func  字段
     * @param value 需要查找的值
     * @return this
     */
    public Children term(Column func, List<?> value) {
        String fieldName = this.convertToEsFieldName(clazz, func);
        EsQueryBuilderUtil.buildTermQuery(boolQueryBuilder, fieldName, value);
        return typedThis;
    }

    /**
     * 精确查询
     *
     * @param func  字段
     * @param value 需要查找的值
     * @return this
     */
    public Children term(Column func, Object value) {
        String fieldName = this.convertToEsFieldName(clazz, func);
        EsQueryBuilderUtil.buildTermQuery(boolQueryBuilder, fieldName, value);
        return typedThis;
    }


    /**
     * 查询嵌套中的字段
     *
     * @param nestedFunc 嵌套字段
     * @param subFunc    嵌套中子字段
     * @return this
     */
    public <C> Children term(Column nestedFunc, SFunction<C, ?> subFunc, List<?> value) {
        // 获取嵌套的字段名称
        String nestedFieldName = this.convertToEsFieldName(clazz, nestedFunc);
        // 获取嵌套中子字段的名称
        String subFieldName = this.getSubEsFieldName(nestedFunc, subFunc);
        EsQueryBuilderUtil.buildNestedTermQuery(boolQueryBuilder, nestedFieldName, subFieldName, value);
        return typedThis;
    }

    /**
     * 查询嵌套中的字段
     *
     * @param nestedFunc 嵌套字段
     * @param subFunc    嵌套中子字段
     * @return this
     */
    public <C> Children term(Column nestedFunc, SFunction<C, ?> subFunc, Object value) {
        // 获取嵌套的字段名称
        String nestedFieldName = this.convertToEsFieldName(clazz, nestedFunc);
        // 获取嵌套中子字段的名称
        String subFieldName = this.getSubEsFieldName(nestedFunc, subFunc);
        EsQueryBuilderUtil.buildNestedTermQuery(boolQueryBuilder, nestedFieldName, subFieldName, value);
        return typedThis;
    }

    /**
     * 不等于
     *
     * @param func  字段
     * @param value 需要查找的值
     * @return this
     */
    public Children termNot(Column func, List<?> value) {
        String fieldName = this.convertToEsFieldName(clazz, func);
        EsQueryBuilderUtil.buildNotTermQuery(boolQueryBuilder, fieldName, value);
        return typedThis;
    }

    /**
     * 不等于
     *
     * @param func  字段
     * @param value 需要查找的值
     * @return this
     */
    public Children termNot(Column func, Object value) {
        String fieldName = this.convertToEsFieldName(clazz, func);
        EsQueryBuilderUtil.buildNotTermQuery(boolQueryBuilder, fieldName, Collections.singletonList(value));
        return typedThis;
    }

    
    /**
     * 区域查询
     *
     * @param func      字段
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return this
     */
    public Children range(Column func, Instant startTime, Instant endTime) {
        String fieldName = this.convertToEsFieldName(clazz, func);
        EsQueryBuilderUtil.buildRangeQuery(boolQueryBuilder, fieldName, startTime, endTime);
        return typedThis;
    }

    /**
     * 获取查询条件，不进行操作
     *
     * @return query
     */
    public Query getQuery() {
        builder.withQuery(boolQueryBuilder);
        return builder.build();
    }
}
